/*
 * @Author: 杰编程 2306248989@qq.com
 * @Date: 2023-08-05 13:25:26
 * @LastEditors: 杰编程 2306248989@qq.com
 * @LastEditTime: 2023-08-06 13:59:48
 * @FilePath: /lesson24/2. tcp/tcp_server.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once

#include <functional>
#include <string>
#include <iostream>

#include <cstring>
#include <cerrno>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>

#include "err.hpp"
#include "ThreadPool.hpp"
#include "task.hpp"
#include "log.hpp"

namespace ns_server
{
    const uint16_t defaultPort = 8080;
    const int backlog = 5;
    class TcpServer;
    struct pthreadData
    {
        pthreadData(TcpServer *ts, int sockfd, const std::string name)
            : _ts(ts), _sockfd(sockfd), _name(name)
        {
        }
        TcpServer *_ts;
        int _sockfd;
        std::string _name;
    };

    using func_t = std::function<std::string(const std::string &)>;
    class TcpServer
    {
    public:
        TcpServer(func_t fun, uint16_t port = defaultPort) : _fun(fun), _port(port)
        {
        }

        void Init()
        {
            // 1. 创建套接字，打开网络文件
            _lisensockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_lisensockfd < 0)
            {
                // std::cerr << "sockfd create error:" << strerror(errno) << std::endl;
                logMessage(Fatal, "sockfd create error:%s\n", strerror(errno));
                exit(SOCKET_ERR);
            }
            // std::cout << "sockfd create successful: " << _lisensockfd << std::endl;
            logMessage(Info, "sockfd create successful:%d\n", _lisensockfd);

            // 2. bind
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = htonl(INADDR_ANY);
            int ret = bind(_lisensockfd, reinterpret_cast<struct sockaddr *>(&local), sizeof(local));
            if (ret != 0)
            {
                // std::cerr << "sockfd bind error:" << strerror(errno) << std::endl;
                logMessage(Fatal, "sockfd bind error:%s\n", strerror(errno));
                exit(BIND_ERR);
            }
            // std::cout << "sockfd bind successful:" << _lisensockfd << std::endl;
            logMessage(Info, "sockfd bind successful:%d\n", _lisensockfd);

            // 3. 监听listen
            ret = listen(_lisensockfd, backlog);
            if (ret != 0)
            {
                // std::cerr << "sockfd listen error:" << strerror(errno) << std::endl;
                logMessage(Fatal, "sockfd listen error:%s\n", strerror(errno));
                exit(LISEN_ERR);
            }
            // std::cout << "sockfd listen successful:" << _lisensockfd << std::endl;
            logMessage(Info, "sockfd listen successful:%d\n", _lisensockfd);
        }

        void Start()
        {
            // std::cout << "开始接受客户" << std::endl;
            logMessage(Info, "开始接受客户\n");
            while (true)
            {
                // 4. 接受客户
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int sockfd = accept(_lisensockfd, reinterpret_cast<struct sockaddr *>(&client), &len);
                if (sockfd < 0)
                {
                    // std::cerr << "accept error" << std::endl;
                    logMessage(Warning, "accept error\n");
                    continue;
                }
                // std::cout << "successfully, accept a clinet: " << sockfd << std::endl;
                logMessage(Info, "successfully, accept a clinet:%d\n", sockfd);
                // 5. 为接上来的用户提供服务
                // 5.1 提取用户的信息
                std::string name;
                std::string clinentip = inet_ntoa(client.sin_addr);
                uint16_t clientport = ntohs(client.sin_port);
                name = clinentip + "-" + std::to_string(clientport);
                // 5.2 提供服务

                // V1版本：单进程
                // Server(sockfd, move(name));

                // V2版本：多进程
                //  pid_t id=fork();
                //  if(id<0)
                //  {
                //      std::cerr<<"子进程创建失败！"<<std::endl;
                //      continue;
                //  }
                //  else if(id==0)
                //  {
                //      //子进程
                //      //建议关闭掉不要的进程
                //      close(_lisensockfd);
                //      //创建孙子进程  ②
                //      if(fork()>0)exit(0);
                //      //子进程对客户进行服务
                //      Server(sockfd, move(name));
                //      exit(0);
                //  }
                //  //父进程
                //  //必须要关闭不需要的fd，防止文件描述符泄漏，最后导致文件描述的数组不够用
                //  close(sockfd);
                //  //父进程需要对子进程进行处理，防止出现僵尸进程，导致内存泄漏的问题
                //  //①进行waitpid等待回收子进程——不推荐，因为这样还是串行执行服务和招揽客户
                //  //②子进程创建孙子进程后退出，父进程立马回收子进程，而孙子进程成为孤儿进程，被1号进程管理。——有一定的意义
                //  //③对SIGCHLD信号进行忽略处理——简单好用，推荐使用
                //  int ret=waitpid(id,nullptr,0);//①②
                //  if(ret==-1)
                //  {
                //      std::cerr<<"回收子进程出错！"<<std::endl;
                //  }
                //  else
                //  {
                //      std::cout<<"wait child "<<id<<" successful"<<std::endl;
                //  }

                // V3版本：多线程
                // pthread_t pid;
                // int ret = pthread_create(&pid, nullptr, ThreadHelper, new pthreadData(this,sockfd,name));
                // if (ret != 0)
                // {
                //     std::cerr << "创建多线程失败，error：" << strerror(ret) << std::endl;
                //     continue;
                // }

                // V4版本：线程池
                // 1. 创建任务
                Task t(std::bind(&TcpServer::Server, this, std::placeholders::_1, std::placeholders::_2), sockfd, name);
                ThreadPool<Task>::getinstance()->pushTask(t);
            }
        }
        static void *ThreadHelper(void *args)
        {
            pthread_detach(pthread_self());
            pthreadData *ptd = static_cast<pthreadData *>(args);
            ptd->_ts->Server(ptd->_sockfd, ptd->_name);
        }
        void Server(int sockfd, std::string name)
        {
            std::cout << sockfd << std::endl;
            // std::cout << "begin sever to" << name << std::endl;
            logMessage(Info, "begin sever to %s\n", name.c_str());
            while (true)
            {
                // 5.2.1 接受信息
                char buffer[1024];
                ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = 0;
                    // std::cout << name << " sends message:" << buffer << std::endl;
                    logMessage(Info, "%s sends message:%s\n", name.c_str(), buffer);
                    // 5.2.2 执行任务
                    std::string tmp = _fun(buffer);
                    //std::cout << "执行任务完成:" << tmp << std::endl;
                    logMessage(Info, "执行任务完成: %s\n", tmp.c_str());

                    // 5.2.3 发送信息
                    ssize_t n = write(sockfd, tmp.c_str(), tmp.size());
                    if (n == -1)
                    {
                        // std::cerr << "发送信息失败：" << tmp << std::endl;
                        logMessage(Error, "发送信息失败：%s\n", tmp.c_str());
                    }
                    else
                    {
                        // std::cout << "发送信息成功：" << tmp << std::endl;
                        logMessage(Info, "发送信息成功：%s\n", tmp.c_str());
                    }
                }
                else if (n == 0)
                {
                    // std::cout << name << " quit ..." << std::endl;
                    logMessage(Info, "%s:quit ...\n", name.c_str());
                    break;
                }
                else
                {
                    // std::cerr << "read error: " << strerror(errno) << std::endl;
                    logMessage(Error, "read error: %s\n", strerror(errno));
                    break;
                }
            }
            close(sockfd);
        }

    private:
        int _lisensockfd;
        uint16_t _port;

        func_t _fun;
    };
}
